package linguo.player;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Stack;

import linguo.Data;
import linguo.Game;
import linguo.level.Board;
import linguo.level.mapObjects.Casillero;
import linguo.level.mapObjects.CasilleroTransitable;
import linguo.level.mapObjects.Coleccionable;
import linguo.level.mapObjects.Pintura;
import linguo.level.mapObjects.pintura.PinturaBlanca;
import linguo.level.mapObjects.pintura.PinturaNegra;

public abstract class Robot {

	protected int x, y;
	protected Orientacion o;
	protected int imageSize = Data.SPRITE_SIZE;
	protected Game game;
	protected Board board;

	protected BufferedImage image;
	protected BufferedImage image_forward, image_down, image_left, image_right;

	protected boolean pincelActivado;
	protected int pincelColor; // 1 = negro, 0 = blanco
	protected int score;
	protected int nafta, max_nafta;
	protected Stack<Coleccionable> storage;

	public Robot(Game game, Board board, int x, int y) {
		this.game = game;
		this.board = board;
		this.x = x;
		this.y = y;
		this.o = Orientacion.UP;
		this.nafta = 50;
		pincelActivado = false;
		pincelColor = 1;
		score = 0;
		storage = new Stack<Coleccionable>();
	}


	/**
	 * Dibuja el robot de acuerdo a su orientacion
	 * 
	 */
	public void render(Graphics g) {

		switch (o) {
			case UP :
				image = image_forward;
				break;
			case DOWN :
				image = image_down;
				break;
			case LEFT :
				image = image_left;
				break;
			case RIGHT :
				image = image_right;
				break;
		}

		g.drawImage(image, x, y, null);
	}

	/**
	 * En caso de poseer suficiente municion dispara un proyectil
	 */
	public abstract void tryToFire();

	/**
	 * Avanza el robot un casillero hacia adelante dependiendo su orientacion
	 */
	public void avanzar() {

		Casillero c = null;
		switch (o) {
			case UP : {
				if (y <= 0) return; // si intenta pedir un casillero fuera del mapa corta
				c = board.getCasillero(x, y - imageSize);
				if (c.isSolid()) return;
				pintar();
				for (int i = 0; i < imageSize; i++) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
					}
					y -= 1;
				}
				break;
			}
			case RIGHT : {
				if (x + imageSize >= game.getWIDTH()) return;
				c = board.getCasillero(x + imageSize, y);
				if (c.isSolid()) return;
				pintar();
				for (int i = 0; i < imageSize; i++) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
					}
					x += 1;
				}
				break;
			}
			case DOWN : {
				if (y + imageSize >= game.getHEIGHT() - imageSize*2) return;
				c = board.getCasillero(x, y + imageSize);
				if (c.isSolid()) return;
				pintar();
				for (int i = 0; i < imageSize; i++) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
					}
					y += 1;
				}
				break;
			}
			case LEFT : {
				if (x <= 0) return;
				c = board.getCasillero(x - imageSize, y);
				if (c.isSolid()) return;
				pintar();
				for (int i = 0; i < imageSize; i++) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
					}
					x -= 1;
				}
				break;
			}
		}

		c.transitar(this);

	}

	/**
	 * Asigna un color de pintura al casillero actual
	 * @param c Casillero a asignar la pintura
	 */
	protected void pintar() {
		if (pincelActivado) {
			Pintura p;
			
			if (pincelColor == 1) p = new PinturaNegra(board, x, y);
			else p = new PinturaBlanca(board, x, y);

			((CasilleroTransitable) board.getCasillero(x, y)).setPintura(p);
		}
	}

	/**
	 * Activa o desactiva el pincel dependiendo del estado anterior
	 */
	public void togglePincel() {
		pincelActivado = !pincelActivado;
	}

	/**
	 * Retorna el estado actual de pincel.
	 * true = activado, false = desactivado
	 * 
	 * @return el estado actual de pincel
	 */
	public boolean getEstadoPincel() {
		return pincelActivado;
	}

	/**
	 * Cambia el color del pincel.
	 * 1 = Negro
	 * 0 = Blanco
	 * 
	 * @param color Denota el color al que se cambiara el pincel
	 */
	public void toggleColorPincel(int color) {
		pincelColor = color;
	}

	/**
	 * Devuelve el color del pincel
	 * @return color del pincel
	 */
	public int getColorPincel() {
		return pincelColor;
	}

	/**
	 * Activa el brazo del robot frente al personaje, recogiendo el premio (si es que existe)
	 */
	public void usarBrazo() {
		Casillero c = casilleroFrente();
		if (c!=null)
		c.recoger(this);
	}

	/**
	 * Deja el ultimo coleccionable tomado en el casillero frente al robot
	 * Si el casillero esta ocupado no hace nada.
	 */
	public void dropColeccionable() {

		if (storage.size() != 0) {
			Casillero c=casilleroFrente();
			
			if (c!=null){
			if (c.isFree()) {
				Coleccionable cl = storage.pop();
				((CasilleroTransitable) c).setObjeto(cl);
				cl.setX(c.getX());
				cl.setY(c.getY());
				cl.droppear();
			}
			}
		}
	}
	
	/**
	 * Devuelve el casillero frente al robot
	 * @return Un casillero si existe un casillero y null en caso de que este fuera del limite del mapa
	 */
	private Casillero casilleroFrente(){
		Casillero c = null;

		switch (o) {
			case UP : {
				if (y - imageSize <= 0) return null;
				c = board.getCasillero(x, y - imageSize);
				break;
			}
			case RIGHT : {
				if (x + imageSize >= game.getWIDTH()) return null;
				c = board.getCasillero(x + imageSize, y);
				break;
			}
			case DOWN : {
				if (y + imageSize >= game.getHEIGHT()) return null;
				c = board.getCasillero(x, y + imageSize);
				break;
			}
			case LEFT : {
				if (x - imageSize <= 0) return null;
				c = board.getCasillero(x - imageSize, y);
				break;
			}
		}
		return c;
	}

	/**
	 * Gira el robot hacia la derecha
	 */
	public void girarDerecha() {
		o = Orientacion.girarDerecha(o);
		board.getCasillero(x, y).transitar(this);
	}

	/**
	 * Gira el robot hacia la izquierda
	 */
	public void girarIzquierda() {
		o = Orientacion.girarIzquierda(o);
		board.getCasillero(x, y).transitar(this);
	}

	/**
	 * Obtiene la posicion en X del Robot.
	 * 
	 * @return la posicion en X
	 */
	public int getX() {
		return x;
	}

	/**
	 * Obtiene la posicion en Y del Robot.
	 * 
	 * @return la posicion en Y
	 */
	public int getY() {
		return y;
	}

	/**
	 * Obtiene la orientacion del Robot.
	 * 
	 * @return Orientacion del robot.
	 */
	public Orientacion getOrientation() {
		return o;
	}

	/**
	 * Obtiene la cantidad de nafta disponible en el Robot.
	 * 
	 * @return Nafta disponible
	 */
	public int getNafta() {
		return nafta;
	}

	/**
	 * Obtiene la cantidad de nafta maxima que puede poseer el Robot.
	 * 
	 * @return Nafta maxima
	 */
	public int getMaxNafta() {
		return max_nafta;
	}

	/**
	 * Obtiene la cantidad de puntos obtenidos hasta el momento.
	 * 
	 * @return Puntos obtenidos
	 */
	public int getScore() {
		return score;
	}

	/**
	 * Obtiene la cantidad actual de municion del robot
	 * 
	 * @return la cantidad actual de municion del robot
	 */
	public abstract int getMunicion();
	
	/**
	 * Obtiene la cantidad de municion maxima que puede poseer el Robot.
	 * 
	 * @return Municion maxima
	 */
	public abstract int getMaxMunicion();

	/**
	 * Setea la posicion en x del robot
	 * 
	 * @param x la nueva posicion en x
	 */
	public void setX(int x) {
		this.x = x;
	}

	/**
	 * Setea la posicion en y del robot
	 * 
	 * @param x la nueva posicion en y
	 */
	public void setY(int y) {
		this.y = y;
	}

	/**
	 * Incrementa la nafta del robot en 'n' unidades.
	 * En caso de superar la capacidad maxima del tanque lo sobrante se descarta.
	 * 
	 * @param n cantidad de nafta a agregar
	 */
	public void incrementarNafta(int n) {
		this.nafta += n;
		if (this.nafta > this.max_nafta) nafta = max_nafta;
	}

	/**
	 * Decrementa la nafta del robot en 'n' unidades
	 * 
	 * @param n cantidad de nafta a quitar
	 */
	public void decrementarNafta(int n) {
		this.nafta -= n;
		if (this.nafta <= 0) game.perderJuego();
	}

	/**
	 * Incrementa los puntos del robot en 's' unidades
	 * 
	 * @param n cantidad de puntos a agregar
	 */
	public void incrementarScore(int s) {
		this.score += s;
	}

	/**
	 * Decrementa los puntos del robot en 's' unidades
	 * 
	 * @param n cantidad de puntos a quitar
	 */
	public void decrementarScore(int s) {
		this.score -= s;
	}

	/**
	 * Incrementa la municion del robot en 'm' unidades.
	 * En caso de superar la capacidad maxima de municion lo sobrante se descarta.
	 * 
	 * @param m cantidad de municion a agregar
	 */
	public abstract void incrementarMunicion(int m);

	/**
	 * Asigna un tablero al robot
	 * 
	 * @param b tablero a asignar al robot
	 */
	public void setBoard(Board b) {
		board = b;
	}

	/**
	 * Agrega un objeto coleccionable al baul
	 * 
	 * @param c coleccionable a agregar
	 */
	public void addColeccionable(Coleccionable c) {
		storage.push(c);
	}

	/**
	 * Retorna el ultimo coleccionable tomado por el robot,
	 * en caso de no haber retorna null
	 * 
	 * @return el ultimo coleccionable tomado por el robot
	 */
	public Coleccionable getLastColeccionable() {
		if (storage.isEmpty()) return null;
		else return storage.peek();
	}

	/**
	 * Retorna la cantidad de coleccionables actualmente en el baul
	 * 
	 * @return la cantidad de coleccionables actualmente en el baul
	 */
	public int getCantColeccionable() {
		return storage.size();
	}
}
